/* eslint-disable max-statements */ import type { MDXComponents } from 'mdx/types'; import type { GetStaticPaths, GetStaticProps } from 'next'; import dynamic from 'next/dynamic'; import Head from 'next/head'; import NextImage from 'next/image'; import { useMemo, type ComponentType, useCallback } from 'react'; import { useIntl } from 'react-intl'; import { Heading, Link, LoadingPage, type MetaValues, Page, PageBody, PageHeader, PageSidebar, ProjectOverview, SharingWidget, SocialLink, Spinner, Time, getLayout, type OverviewMeta, } from '../../components'; import { mdxComponents } from '../../components/mdx'; import { fetchGithubRepoMeta } from '../../services/github'; import styles from '../../styles/pages/projects.module.scss'; import type { GithubRepositoryMeta, Maybe, NextPageWithLayout, Project, } from '../../types'; import { CONFIG } from '../../utils/config'; import { capitalize, getSchemaFrom, getWebPageGraph, } from '../../utils/helpers'; import { type Messages, getProjectData, getProjectFilenames, loadTranslation, } from '../../utils/helpers/server'; import { useBreadcrumbs, useGithubRepoMeta, useHeadingsTree, } from '../../utils/hooks'; const Toc = dynamic( async () => import('../../components').then((mod) => mod.TocWidget), { ssr: false, } ); const getGithubRepoInputFrom = (namespace: string) => { const parts = namespace.split('/'); if (parts.length !== 2) throw new Error( 'Invalid repo. It should use the following format: owner/name.' ); return { owner: parts[0], name: parts[1] }; }; const isValidRepo = (name: string): name is 'github' | 'gitlab' => ['github', 'gitlab'].includes(name); type ProjectPageProps = { data: { githubMeta: Maybe; project: Project; }; translation: Messages; }; /** * Project page. */ const ProjectPage: NextPageWithLayout = ({ data }) => { const { id, intro, meta, slug, title } = data.project; const intl = useIntl(); const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumbs(title); const { ref, tree } = useHeadingsTree({ fromLevel: 2 }); const { isLoading: isGithubMetaLoading, meta: githubMeta } = useGithubRepoMeta( meta.repos.github ? getGithubRepoInputFrom(meta.repos.github) : null, data.githubMeta ); const page = { title: `${meta.seo.title} - ${CONFIG.name}`, url: `${CONFIG.url}${slug}`, }; const jsonLd = getSchemaFrom([ getWebPageGraph({ breadcrumb: breadcrumbSchema, copyrightYear: new Date(meta.dates.publication).getFullYear(), cover: `/projects/${id}.jpg`, dates: meta.dates, description: intro, slug, title, }), ]); const messages = { repos: { github: intl.formatMessage({ defaultMessage: 'Github', description: 'ProjectPage: Github repo label', id: 'l82UU5', }), gitlab: intl.formatMessage({ defaultMessage: 'Gitlab', description: 'ProjectPage: Gitlab repo label', id: '1msHuZ', }), }, widgets: { sharingTitle: intl.formatMessage({ defaultMessage: 'Share', id: 'JnalJp', description: 'ProjectPage: sharing widget title', }), tocTitle: intl.formatMessage({ defaultMessage: 'Table of Contents', description: 'PageLayout: table of contents title', id: 'eys2uX', }), }, }; const getAdditionalMeta = useCallback( ( repo: string, repoMeta: Maybe ): Partial => { const loading = { creationDate: intl.formatMessage({ defaultMessage: 'Loading the repository creation date...', description: 'ProjectPage: loading repository metadata', id: 'OzVOKP', }), popularity: intl.formatMessage({ defaultMessage: 'Loading the repository popularity...', description: 'ProjectPage: loading repository metadata', id: 'RfXzNe', }), updateDate: intl.formatMessage({ defaultMessage: 'Loading the repository update date...', description: 'ProjectPage: loading repository metadata', id: 'VEHEEs', }), }; const stars = intl.formatMessage( { defaultMessage: '{starsCount, plural, =0 {No stars} one {# star} other {# stars}}', description: 'ProjectPage: stars count', id: '4M71hp', }, { starsCount: repoMeta?.stargazerCount } ); const popularityURL = `https://github.com/${repo}/stargazers`; return { creationDate: isGithubMetaLoading || !repoMeta ? ( ) : (